package com.wangzy.myairobot.ui;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.idst.nui.AsrResult;
import com.alibaba.idst.nui.Constants;
import com.alibaba.idst.nui.INativeNuiCallback;
import com.alibaba.idst.nui.KwsResult;
import com.alibaba.idst.nui.NativeNui;
import com.wangzy.myairobot.Auth;
import com.wangzy.myairobot.R;
import com.wangzy.myairobot.Utils;
import com.wangzy.myairobot.serial.UsbDeviceHelper;

import java.util.concurrent.atomic.AtomicBoolean;

// 本样例展示在线实时语音识别使用方法
// Android SDK 详细说明：https://help.aliyun.com/document_detail/174190.html
//6c451bb284cb4acd881fffe9d32b5cf7
//https://nls-portal.console.aliyun.com/overview?spm=5176.12157770.J_5524031460.4.e75f6141rILmAG
public class MainActivity extends Activity implements INativeNuiCallback {


    private static final String TAG = "aisdk";
    NativeNui nui_instance = new NativeNui();
    final static int WAVE_FRAM_SIZE = 20 * 2 * 1 * 16000 / 1000; //20ms audio for 16k/16bit/mono
    public final static int SAMPLE_RATE = 16000;
    private AudioRecord mAudioRecorder;

    private Button startButton;
    private Button cancelButton;
    private Switch mVadSwitch;
    private TextView asrView;
    private TextView textViewStatus;
    private boolean mInit = false;
    private boolean mStopping = false;
    private final AtomicBoolean vadMode = new AtomicBoolean(false);
    private final String[] permissions = {Manifest.permission.RECORD_AUDIO};

   void setTextStatus(String text){
        textViewStatus.post(() -> textViewStatus.setText(text));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // 检查该权限是否已经获取
            int i = ContextCompat.checkSelfPermission(this, permissions[0]);
            // 权限是否已经 授权 GRANTED---授权  DINIED---拒绝
            if (i != PackageManager.PERMISSION_GRANTED) {
                // 如果没有授予该权限，就去提示用户请求
                this.requestPermissions(permissions, 321);
            }
            while (true) {
                i = ContextCompat.checkSelfPermission(this, permissions[0]);
                // 权限是否已经 授权 GRANTED---授权  DINIED---拒绝
                if (i == PackageManager.PERMISSION_GRANTED) break;
            }
        }
        String version = NativeNui.GetInstance().GetVersion();
        Log.i(TAG, "current sdk version: " + version);
        ToastText("内部SDK版本号:" + version);
        initUIWidgets();
    }

    private void listUsbDevices(){

        UsbDeviceHelper usbDeviceHelper=new UsbDeviceHelper(this);
        usbDeviceHelper.listUsbDevices();

    }



    @Override
    protected void onStart() {
        Log.i(TAG, "onStart");
        super.onStart();
        doInit();
    }



    @Override
    protected void onStop() {
        Log.i(TAG, "onStop");
        super.onStop();
        nui_instance.release();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    private void initUIWidgets() {

        asrView = (TextView) findViewById(R.id.textView);
        asrView.setMovementMethod(new ScrollingMovementMethod());

        textViewStatus= (TextView) findViewById(R.id.textViewStatus);

        startButton = (Button) findViewById(R.id.button_start);
        cancelButton = (Button) findViewById(R.id.button_cancel);

        mVadSwitch = (Switch) findViewById(R.id.vad_switch);
        mVadSwitch.setVisibility(View.GONE);
        mVadSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
            Log.i(TAG, "vad mode onCheckedChanged b=" + b);
            vadMode.set(b);
        });

        setButtonState(startButton, true);
        setButtonState(cancelButton, false);

        startButton.setOnClickListener(v -> {
            Log.i(TAG, "start!!!");
            setButtonState(startButton, false);
            setButtonState(cancelButton, true);
            showText(asrView, "");
            startDialog();
        });

        cancelButton.setOnClickListener(v -> {
            Log.i(TAG, "cancel");
            if (!checkNotInitToast()) {
                return;
            }

            setButtonState(startButton, true);
            setButtonState(cancelButton, false);
            getWindow().getDecorView().post(() -> {
                mStopping = true;
                long ret = nui_instance.stopDialog();
                Log.i(TAG, "cancel dialog " + ret + " end");
            });

        });

        findViewById(R.id.buttonControll).setOnClickListener(view -> {
            Intent intent=new Intent(MainActivity.this,ControllAcivity.class);
            startActivity(intent);
        });

        findViewById(R.id.buttonListDevice).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                listUsbDevices();
            }
        });
    }

    private void doInit() {
        showText(asrView, "");
        setButtonState(startButton, true);
        setButtonState(cancelButton, false);

        //这里主动调用完成SDK配置文件的拷贝, 即将nuisdk.aar中assets中资源文件拷贝到cache目录
        // 注意: V2.6.2版本开始纯云端功能可不需要资源文件
//        if (CommonUtils.copyAssetsData(this)) {
//            Log.i(TAG, "copy assets data done");
//        } else {
//            Log.i(TAG, "copy assets failed");
//            return;
//        }

        //获取工作路径, 即获得拷贝后资源文件存储的cache路径, 作为workspace
        // 注意: V2.6.2版本开始纯云端功能可不需要资源文件
        String assets_path = "";
//        assets_path = CommonUtils.getModelPath(this);
//        Log.i(TAG, "use workspace " + assets_path);

        String debug_path = getExternalCacheDir().getAbsolutePath() + "/debug_" + System.currentTimeMillis();
        Utils.createDir(debug_path);

        //录音初始化，录音参数中格式只支持16bit/单通道，采样率支持8K/16K
        //使用者请根据实际情况选择Android设备的MediaRecorder.AudioSource
        //录音麦克风如何选择, 可查看https://developer.android.google.cn/reference/android/media/MediaRecorder.AudioSource
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        mAudioRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, WAVE_FRAM_SIZE * 4);

        //初始化SDK，注意用户需要在Auth.getTicket中填入相关ID信息才可以使用。
        int ret = nui_instance.initialize(this, genInitParams(assets_path, debug_path), Constants.LogLevel.LOG_LEVEL_VERBOSE, true);
        Log.i(TAG, "result = " + ret);
        if (ret == Constants.NuiResultCode.SUCCESS) {
            mInit = true;
        } else {
            ToastText(Utils.getMsgWithErrorCode(ret, "init"));
        }
        //设置相关识别参数，具体参考API文档
        nui_instance.setParams(genParams());
    }

    private String genParams() {
        String params = "";
        try {
            JSONObject nls_config = new JSONObject();
            nls_config.put("enable_intermediate_result", true);
//            参数可根据实际业务进行配置
//            接口说明可见https://help.aliyun.com/document_detail/173528.html
//            查看 2.开始识别
//            nls_config.put("enable_punctuation_prediction", true);
//            nls_config.put("enable_inverse_text_normalization", true);
//            nls_config.put("max_sentence_silence", 800);
//            nls_config.put("enable_words", false);
//            nls_config.put("sample_rate", 16000);
//            nls_config.put("sr_format", "opus");

            /*若文档中不包含某些参数，但是此功能支持这个参数，可以用如下万能接口设置参数*/
//            JSONObject extend_config = new JSONObject();
//            extend_config.put("custom_test", true);
//            nls_config.put("extend_config", extend_config);

            JSONObject tmp = new JSONObject();
            tmp.put("nls_config", nls_config);
            tmp.put("service_type", Constants.kServiceTypeSpeechTranscriber); // 必填
//            如果有HttpDns则可进行设置
//            tmp.put("direct_ip", Utils.getDirectIp());
            params = tmp.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return params;
    }

    private void startDialog() {
        getWindow().getDecorView().post(() -> {
            int ret = nui_instance.startDialog(Constants.VadMode.TYPE_P2T, genDialogParams());
            Log.i(TAG, "start done with " + ret);
        });
    }

    private String genInitParams(String workpath, String debug_path) {
        String str = "";
        try {
            //获取账号访问凭证：
            JSONObject object = Auth.getTicket(Auth.GetTicketMethod.GET_TOKEN_FROM_SERVER_FOR_ONLINE_FEATURES);
            if (!object.containsKey("token")) {
                Log.e(TAG, "Cannot get token!!!");
            }

            object.put("device_id", Utils.getDeviceId()); // 必填, 推荐填入具有唯一性的id, 方便定位问题
            object.put("url", "wss://nls-gateway.cn-shanghai.aliyuncs.com:443/ws/v1"); // 默认

            //工作目录路径，SDK从该路径读取配置文件
//            object.put("workspace", workpath); // V2.6.2版本开始纯云端功能可不设置workspace

            //当初始化SDK时的save_log参数取值为true时，该参数生效。表示是否保存音频debug，该数据保存在debug目录中，需要确保debug_path有效可写。
            object.put("save_wav", "true");
            //debug目录，当初始化SDK时的save_log参数取值为true时，该目录用于保存中间音频文件。
            object.put("debug_path", debug_path);

            // FullMix = 0   // 选用此模式开启本地功能并需要进行鉴权注册
            // FullCloud = 1
            // FullLocal = 2 // 选用此模式开启本地功能并需要进行鉴权注册
            // AsrMix = 3    // 选用此模式开启本地功能并需要进行鉴权注册
            // AsrCloud = 4
            // AsrLocal = 5  // 选用此模式开启本地功能并需要进行鉴权注册
            // 这里只能选择FullMix和FullCloud
            object.put("service_mode", Constants.ModeFullCloud); // 必填
            str = object.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        // 注意! str中包含ak_id ak_secret token app_key等敏感信息, 实际产品中请勿在Log中输出这类信息！
        Log.i(TAG, "InsideUserContext:" + str);
        return str;
    }

    private String genDialogParams() {
        String params = "";
        try {
            JSONObject dialog_param = new JSONObject();
            // 运行过程中可以在startDialog时更新临时参数，尤其是更新过期token
            // 注意: 若下一轮对话不再设置参数，则继续使用初始化时传入的参数
            long distance_expire_time_4h = 14400;
            dialog_param = Auth.refreshTokenIfNeed(dialog_param, distance_expire_time_4h);
            // 注意: 若需要更换appkey和token，可以直接传入参数
            dialog_param.put("app_key", "FwZOmUN4Y4dhJ3ED");
//            dialog_param.put("token", "7da32b932ea44078815723aadc770e0e");
            dialog_param.put("token", "6c451bb284cb4acd881fffe9d32b5cf7");
            params = dialog_param.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        Log.i(TAG, "dialog params: " + params);
        return params;
    }

    private boolean checkNotInitToast() {
        if (!mInit) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "SDK未成功初始化.", Toast.LENGTH_LONG).show();
                }
            });
            return false;
        } else {
            return true;
        }
    }

    private void setButtonState(final Button btn, final boolean state) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "setBtn state " + btn.getText() + " state=" + state);
                btn.setEnabled(state);
            }
        });
    }

    private void showText(final TextView who, final String text) {
//        {
//            "header": {
//            "namespace": "SpeechTranscriber",
//                    "name": "TranscriptionResultChanged",
//                    "status": 20000000,
//                    "message_id": "15cc266e368340d58e0a1be1b5512dd4",
//                    "task_id": "0d2ce1ed0f104235968388ac192a06f6",
//                    "status_text": "Gateway:SUCCESS:Success."
//        },
//            "payload": {
//            "index": 1,
//                    "time": 1680,
//                    "result": "嗨龙",
//                    "confidence": 0.847,
//                    "words": [
//
//        ],
//            "status": 0,
//                    "fixed_result": "",
//                    "unfixed_result": ""
//        }
//        }

        Log.w(TAG, "---->>识别结果:" + text);

        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                String result = "";
                if (text.startsWith("{")) {
                    JSONObject resultJso = JSONObject.parseObject(text);
                    JSONObject payLoad = resultJso.getJSONObject("payload");
                    result = payLoad.getString("result");
                }

                Log.i(TAG, "showText text=" + text);
                if (TextUtils.isEmpty(result)) {
                    Log.w(TAG, "asr text is empty");
                    who.setText("识别文本");
                } else {
                    who.setText(text);
                    who.setText(result);
                }
            }
        });
    }

    private void ToastText(String text) {
        final String str = text;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
            }
        });
    }

    private void appendText(final TextView who, final String text) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "append text=" + text);
                if (TextUtils.isEmpty(text)) {
                    return;
                } else {
                    String orign = who.getText().toString();
                    who.setText(orign + "\n---\n" + text);
                }
            }
        });
    }

    //当回调事件发生时调用
    @Override
    public void onNuiEventCallback(Constants.NuiEvent event, final int resultCode, final int arg2, KwsResult kwsResult, AsrResult asrResult) {
        Log.i(TAG, "回调事件event=" + event + " resultCode=" + resultCode);
        // asrResult包含task_id，task_id有助于排查问题，请用户进行记录保存。

        Log.d(TAG,"-->"+event);
        if(event==Constants.NuiEvent.EVENT_SENTENCE_START){
            Log.d(TAG,"句子识别开始:"+event);
        }else if(event==Constants.NuiEvent.EVENT_SENTENCE_END){
            Log.d(TAG,"句子识别结束"+event);
        }

        if (event == Constants.NuiEvent.EVENT_TRANSCRIBER_COMPLETE) {

            setButtonState(startButton, true);
            setButtonState(cancelButton, false);
            appendText(asrView, asrResult.allResponse);
            mStopping = false;
            setTextStatus("EVENT_TRANSCRIBER_COMPLETE");

        } else if (event == Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT || event == Constants.NuiEvent.EVENT_SENTENCE_END) {

            if (mStopping) {
                appendText(asrView, asrResult.asrResult);
            } else {
                showText(asrView, asrResult.asrResult);
            }
            setTextStatus("EVENT_ASR_PARTIAL_RESULT|EVENT_SENTENCE_END");

        } else if (event == Constants.NuiEvent.EVENT_VAD_START) {
            showText(asrView, "EVENT_VAD_START");
            Log.d(TAG,"EVENT_VAD_START");
            setTextStatus("EVENT_VAD_START");
        } else if (event == Constants.NuiEvent.EVENT_VAD_END) {
            appendText(asrView, "EVENT_VAD_END");
            Log.d(TAG,"EVENT_VAD_END");
            setTextStatus("EVENT_VAD_END");
        } else if (event == Constants.NuiEvent.EVENT_ASR_ERROR) {
            // asrResult在EVENT_ASR_ERROR中为错误信息，搭配错误码resultCode和其中的task_id更易排查问题，请用户进行记录保存。
            if (mStopping) {
                appendText(asrView, asrResult.asrResult);
            } else {
                showText(asrView, asrResult.asrResult);
            }

            runOnUiThread(() -> Toast.makeText(MainActivity.this, "ERROR with " + resultCode, Toast.LENGTH_LONG).show());
            final String msg_text = Utils.getMsgWithErrorCode(resultCode, "start");

            runOnUiThread(() -> Toast.makeText(MainActivity.this, msg_text, Toast.LENGTH_LONG).show());

            setButtonState(startButton, true);
            setButtonState(cancelButton, false);
            mStopping = false;
        } else if (event == Constants.NuiEvent.EVENT_MIC_ERROR) {
            // EVENT_MIC_ERROR表示2s未传入音频数据，请检查录音相关代码、权限或录音模块是否被其他应用占用。
            final String msg_text = Utils.getMsgWithErrorCode(resultCode, "start");
            runOnUiThread(() -> Toast.makeText(MainActivity.this, msg_text, Toast.LENGTH_LONG).show());
            setButtonState(startButton, true);
            setButtonState(cancelButton, false);
            // 此处也可重新启动录音模块
        } else if (event == Constants.NuiEvent.EVENT_DIALOG_EX) { /* unused */
            Log.i(TAG, "dialog extra message = " + asrResult.asrResult);
        }
    }

    //当调用NativeNui的start后，会一定时间反复回调该接口，底层会提供buffer并告知这次需要数据的长度
    //返回值告知底层读了多少数据，应该尽量保证return的长度等于需要的长度，如果返回<=0，则表示出错
    @Override
    public int onNuiNeedAudioData(byte[] buffer, int len) {
        if (mAudioRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
            Log.e(TAG, "audio recorder not init");
            return -1;
        }
        return mAudioRecorder.read(buffer, 0, len);
    }

    //当录音状态发送变化的时候调用
    @Override
    public void onNuiAudioStateChanged(Constants.AudioState state) {
        Log.i(TAG, "onNuiAudioStateChanged");
        if (state == Constants.AudioState.STATE_OPEN) {
            Log.i(TAG, "audio recorder start");
            mAudioRecorder.startRecording();
            Log.i(TAG, "audio recorder start done");
        } else if (state == Constants.AudioState.STATE_CLOSE) {
            Log.i(TAG, "audio recorder close");
            mAudioRecorder.release();
        } else if (state == Constants.AudioState.STATE_PAUSE) {
            Log.i(TAG, "audio recorder pause");
            mAudioRecorder.stop();
        }
    }

    @Override
    public void onNuiAudioRMSChanged(float val) {
//        Log.i(TAG, "onNuiAudioRMSChanged vol " + val);
    }

    @Override
    public void onNuiVprEventCallback(Constants.NuiVprEvent event) {
        Log.i(TAG, "onNuiVprEventCallback event " + event);
    }
}



